package com.itcoon.cloud.framework.transform;


import com.itcoon.cloud.framework.transform.enums.TransformFeature;
import com.itcoon.cloud.framework.transform.register.ModelDefinitionRegister;
import com.itcoon.cloud.framework.transform.utils.TransformServiceUtils;
import com.itcoon.cloud.framework.transform.config.TransformConfigurationProperties;
import com.itcoon.cloud.framework.transform.definition.InjectDefinition;
import com.itcoon.cloud.framework.transform.definition.ModelDefinition;
import com.itcoon.cloud.framework.transform.pool.AssembleBeanHolder;

import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author a0000
 */
public abstract class AbstractTransformHandler<T, R> implements TransformHandler<T, R> {

     protected final TransformConfigurationProperties properties;

     public AbstractTransformHandler(TransformConfigurationProperties properties){
         this.properties = properties;
     }

     @Override
     public R handle(Class<R> targetClass, Type[] actualTypes, R r, T source, String[] ignoreProperties, List<InjectDefinition> injectDefinitions, TransformFeature... features){
          if(source == null){
               return null;
          }

          if(Modifier.isFinal(targetClass.getModifiers()) || targetClass.isPrimitive()){
               return (R)source;
          }


          List<TransformFeature> defaultFeatures = new ArrayList<>(Arrays.asList(properties.getFeatures()));
          List<TransformFeature> featureList = new CopyOnWriteArrayList<>(Arrays.asList(features));
          for (TransformFeature defaultFeature : defaultFeatures) {
               long count = featureList.stream().filter(f-> Objects.equals(f.value(), defaultFeature.value())).count();
               if(count == 0){
                    featureList.add(defaultFeature);
               }
          }

          ModelDefinition md = getAndCacheModelDefinition(targetClass, source);
          return doHandle(md, targetClass, actualTypes, r, source, ignoreProperties, injectDefinitions, featureList.toArray(new TransformFeature[0]));
     }

     protected abstract R doHandle(ModelDefinition md, Class<R> targetClass, Type[] actualTypes, R target, T source, String[] ignoreProperties, List<InjectDefinition> injectDefinitions, TransformFeature... features);

     private <T, R> ModelDefinition getAndCacheModelDefinition(Class<R> targetClass, T source){
          AssembleBeanHolder assembleBeanHolder = AssembleBeanHolder.getInstance();
          ModelDefinition md = AssembleBeanHolder.getInstance().getModelDefinition(targetClass, source);
          if(md ==null){
               ModelDefinitionRegister modelDefinitionRegister = new ModelDefinitionRegister();
               modelDefinitionRegister.register(targetClass, TransformServiceUtils.getOriginClass(source));
               md = assembleBeanHolder.getModelDefinition(targetClass, source);
          }
          return md;
     }

}
